package article

import (
	"context"
	"errors"
	"github.com/bwmarrin/snowflake"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
	"time"
	"webook/internal/repository/dao"
)

type MongoArticleDAO struct {
	col     *mongo.Collection // 制作库
	liveCol *mongo.Collection // 线上库
	node    *snowflake.Node
}

func (m *MongoArticleDAO) ListPub(ctx context.Context, offset int, limit int) ([]dao.Article, error) {
	//TODO implement me
	panic("implement me")
}

func (m *MongoArticleDAO) GetPubById(ctx context.Context, articleId int64) (dao.PublishArticle, error) {
	//TODO implement me
	panic("implement me")
}

func (m *MongoArticleDAO) GetById(ctx context.Context, authorId int64, articleId int64) (dao.Article, error) {
	//TODO implement me
	panic("implement me")
}

func (m *MongoArticleDAO) FindByAuthorId(ctx context.Context, id int64, offset int, limit int) ([]dao.Article, error) {
	//TODO implement me
	panic("implement me")
}

func NewMongoArticleDAO(col *mongo.Collection, liveCol *mongo.Collection, node *snowflake.Node) dao.ArticleDAO {
	return &MongoArticleDAO{
		col:     col,
		liveCol: liveCol,
		node:    node,
	}
}

func (m *MongoArticleDAO) Upsert(ctx context.Context, article dao.PublishArticle) (int64, error) {
	// 实现mongodb的upsert语法
	filter := bson.D{bson.E{Key: "id", Value: article.Id}}
	now := time.Now().UnixMilli()
	updateDoc := bson.D{
		// 更新
		bson.E{Key: "$set", Value: bson.M{
			"title":   article.Title,
			"content": article.Content,
			"utime":   now,
		}},
		// 新增的额外字段
		bson.E{Key: "$setOnInsert", Value: bson.M{
			"id":        article.Id,
			"author_id": article.AuthorId,
			"ctime":     now,
			"utime":     now,
		}},
	}
	_, err := m.col.UpdateOne(ctx, filter, updateDoc, options.UpdateOne().SetUpsert(true))
	if err != nil {
		return 0, err
	}
	return article.Id, nil
}

func (m *MongoArticleDAO) Insert(ctx context.Context, article dao.Article) (int64, error) {
	// 插入数据
	// 自增主键怎么办？
	// 策略1：改造代码为string类型并使用objectId
	// 策略2：使用GUID
	// 策略3：使用雪花算法生成id
	id := m.node.Generate().Int64()
	article.Id = id
	now := time.Now().UnixMilli()
	article.Ctime = now
	article.Utime = now
	_, err := m.col.InsertOne(ctx, article)
	if err != nil {
		return 0, err
	}
	return id, nil
}

func (m *MongoArticleDAO) Update(ctx context.Context, article dao.Article) (int64, error) {
	// 修改制作库
	filter := bson.D{
		bson.E{Key: "id", Value: article.Id},
		bson.E{Key: "author_id", Value: article.AuthorId},
	}
	now := time.Now().UnixMilli()
	updateDoc := bson.D{bson.E{Key: "$set", Value: bson.M{
		"title":   article.Title,
		"content": article.Content,
		"utime":   now,
	}}}
	res, err := m.col.UpdateOne(ctx, filter, updateDoc)
	if err != nil {
		return 0, err
	}
	if res.ModifiedCount == 0 {
		// 创作者不对，说明有人在瞎搞
		return 0, errors.New("ID 不对或者创作者不对")
	}
	return article.Id, nil
}

func (m *MongoArticleDAO) Sync(ctx context.Context, article dao.Article) (int64, error) {
	// mongodb的事务怎么办？
	// 1. 先操作制作库
	var (
		id  int64
		err error
	)
	if article.Id == 0 {
		id, err = m.Insert(ctx, article)
	} else {
		id, err = m.Update(ctx, article)
	}
	if err != nil {
		// 操作制作库出现问题
		return 0, err
	}
	// 2. 操作线上库
	article.Id = id
	// 实现mongodb的upsert语法
	filter := bson.D{bson.E{Key: "id", Value: article.Id}}
	now := time.Now().UnixMilli()
	updateDoc := bson.D{
		// 更新
		bson.E{Key: "$set", Value: bson.M{
			"title":   article.Title,
			"content": article.Content,
			"utime":   now,
		}},
		// 新增的额外字段
		bson.E{Key: "$setOnInsert", Value: bson.M{
			"id":        article.Id,
			"author_id": article.AuthorId,
			"ctime":     now,
			"utime":     now,
		}},
	}
	_, err = m.liveCol.UpdateOne(ctx, filter, updateDoc, options.UpdateOne().SetUpsert(true))
	if err != nil {
		return 0, err
	}
	return article.Id, nil
}
